package com.eelye.util.winui.window;

import com.sun.jna.Memory;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
import com.sun.jna.platform.win32.User32;
import com.sun.jna.platform.win32.WinDef;
import com.sun.jna.platform.win32.WinDef.HWND;
import com.sun.jna.platform.win32.WinNT;

import java.util.Arrays;
import java.util.List;

public class TreeView extends Window {

    public static final int TVIF_TEXT = 0x0001;
    public static final int TVM_GETITEMW = 0x110D;
    public static final int TVE_COLLAPSE = 0x0002;
    public static final int TVE_EXPAND = 0x0001;
    public static final int TVM_EXPAND = 0x1101;
    public static final int TVM_SELECTITEM = 0x1102;
    public static final int TVGN_CARET = 0x0000000c;
    public static final int TVM_GETNEXTITEM = 0x1107;
    public static final int TVGN_NEXT = 0x0001;
    public static final int TVGN_CHILD = 0x0004;
    public static final int TVGN_ROOT = 0x0000;

    User32 user32 = User32.INSTANCE;

    public TreeView(HWND hWnd) {
        super(hWnd, "SysTreeView32");
    }


    public static class HTREEITEM extends WinNT.HANDLE {

        public HTREEITEM() {
            super();
        }

        public HTREEITEM(Pointer p) {
            super(p);
        }
    }

    public static class TVITEM extends Structure {
        public int mask;
        public HTREEITEM hItem;
        public int state;
        public int stateMask;
        public Pointer pszText;
        public int cchTextMax;
        public int iImage;
        public int iSelectedImage;
        public int cChildren;
        public WinDef.LPARAM lParam;
        public int iIntegral;
        public int uStateEx;
        public int hwnd;
        public int iExpandedImage;

        public TVITEM() {
            super();

            Memory m = new Memory(260);
            mask = 0; //code for LVIF_TEXT
            pszText = m.getPointer(0);
            cchTextMax = 260;
            iImage = 0;
            lParam = new WinDef.LPARAM(0);
        }

        protected List<String> getFieldOrder() {
            return Arrays.asList(
                    "mask", "hItem", "state", "stateMask", "pszText", "cchTextMax",
                    "iImage", "iSelectedImage", "cChildren", "lParam", "iIntegral",
                    "uStateEx", "hwnd", "iExpandedImage"
            );
        }
    }

    public boolean selectMenu(String ... nodes) {
        HTREEITEM currentItem = null;
        for (String node : nodes) {
            currentItem = findNode(hWnd, currentItem, node.trim());
            if (currentItem == null) {
                return false;
            }

            expand(currentItem);
            selectItem(currentItem);
        }

        return true;
    }

    protected void selectItem(HTREEITEM currentItem) {
        WinDef.LPARAM currentItemPointer = new WinDef.LPARAM(currentItem.getPointer().getLong(0));
        User32.INSTANCE.SendMessage(hWnd, TVM_SELECTITEM, new WinDef.WPARAM(TVGN_CARET), currentItemPointer);
    }

    protected void expand(HTREEITEM currentItem) {
        WinDef.LPARAM currentItemPointer = new WinDef.LPARAM(currentItem.getPointer().getLong(0));
        user32.SendMessage(hWnd, TVM_EXPAND, new WinDef.WPARAM(TVE_EXPAND), currentItemPointer);
    }

    protected HTREEITEM findNode(HWND hTreeView, HTREEITEM item, String nodeName) {
        HTREEITEM hItem = getChild(item);

        while (hItem != null) {
            Memory mem = new Memory(256);
            TVITEM tvItem = new TVITEM();
            tvItem.mask = TVIF_TEXT;
            tvItem.pszText = mem;
            tvItem.cchTextMax = (int) mem.size();
            tvItem.hItem = hItem;

            WinDef.LPARAM lParam = new WinDef.LPARAM(tvItem.getPointer().getLong(0));
            if (user32.SendMessage(hTreeView, TVM_GETITEMW, null, lParam).intValue() != 0) {
                String itemName = mem.toString().trim();
                if (itemName.equals(nodeName)) {
                    return hItem;
                }
            }

            hItem = getNextSibling(hItem);
        }

        return null;
    }

    public HTREEITEM getChild(HTREEITEM item) {
        TVITEM tvi = new TVITEM();
        tvi.hItem = new HTREEITEM(Pointer.NULL);

        WinDef.LRESULT ret = User32.INSTANCE.SendMessage(
                hWnd,
                TVM_GETNEXTITEM,
                new WinDef.WPARAM(TVGN_CHILD), // 查找子项
                new WinDef.LPARAM(0)
        );

        return new HTREEITEM(ret.toPointer());
    }

    protected HTREEITEM getNextSibling(HTREEITEM startAfter) {
        TVITEM tvi = new TVITEM();
        Pointer lParam = tvi.getPointer();
        WinDef.LRESULT ret = User32.INSTANCE.SendMessage(
                hWnd,
                TVM_GETNEXTITEM,
                new WinDef.WPARAM(TVGN_NEXT), // 查找子项
                new WinDef.LPARAM(lParam.getLong(0)) // TVITEM 结构体的地址
        );

        return tvi.hItem;
    }
}
